package calao.client.ui;

import java.util.Date;

import calao.client.Desktop;
import calao.client.util.CalendarDate;

import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.ui.ChangeListenerCollection;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.SourcesChangeEvents;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.Widget;

public class DateBox 
extends Composite
implements CalendarListener, KeyboardListener, SourcesChangeEvents, Validatable
{

	static DateTimeFormat format = DateTimeFormat.getShortDateFormat();

	static final String STYLE = "calao-DateBox";

	private final Calendar calendar = new Calendar(null, false);
	private final PopupPanel popup = new PopupPanel(true);
	private final TextBox box = new TextBox();
	private final PushButton button = 
		new PushButton(Desktop.getDesktop().images.calendar().createImage());

	private final ChangeListenerCollection listeners = new ChangeListenerCollection();

	boolean emptyAllowed = false;

	String parsedString;

	public DateBox(boolean showWeekends)
	{
		HorizontalPanel panel = new HorizontalPanel();
		button.addClickListener(new ClickListener() {
			public void onClick(Widget sender) {
				showPopup();
			}
		});
		panel.addStyleName(STYLE);
		calendar.addListener(this);
		popup.add(calendar);
		box.addKeyboardListener(this);
		panel.add(box);
		panel.add(button);
		initWidget(panel);
		if (!showWeekends) hideWeekends();
	}
	
	public DateBox()
	{
		this(true);
	}

	private void showPopup() 
	{
		calendar.showSelectedDate();
		calendar.refresh();
		popup.setPopupPosition(button.getAbsoluteLeft(), button.getAbsoluteTop());
		popup.show();
	}

	public void onSelect(Date date) 
	{
		box.setText(parsedString = format.format(date));
		listeners.fireChange(this);
		popup.hide();
	}

	void parseDate()
	{
		if (parsedString==null || !parsedString.equals(box.getText())) {
			if (isEmpty()) {
				calendar.setSelectedDate(null);
				parsedString = null;
			}
			else try {
				calendar.setSelectedDate(format.parse(box.getText()));
				parsedString = box.getText();
			}
			catch (IllegalArgumentException e) {
				String pattern = format.getPattern();
				if (pattern.endsWith("y")) {
					// try again by appending year				
					String text = box.getText();
					int pos = pattern.length()-1;
					while (pattern.charAt(pos)=='y') pos--;
					text += pattern.charAt(pos);
					text += new CalendarDate().getYear();
					try {
						calendar.setSelectedDate(format.parse(text));
						parsedString = box.getText();
						return;
					}
					catch (IllegalArgumentException ebis) { }
				}
				calendar.setSelectedDate(null);
				parsedString = null;
			}
		}
	}

	public void addChangeListener(ChangeListener listener) 
	{
		listeners.add(listener);
	}

	public void removeChangeListener(ChangeListener listener) 
	{
		listeners.remove(listener);
	}

	public void onKeyDown(Widget sender, char keyCode, int modifiers) { }

	public void onKeyPress(Widget sender, char keyCode, int modifiers) { }

	public void onKeyUp(Widget sender, char keyCode, int modifiers) 
	{
		listeners.fireChange(this);
	}
	
	public Date getDate()
	{
		parseDate();
		return calendar.getSelectedDate();
	}
	
	public void setDate(long time)
	{
		setDate(time>0? new Date(time) : null);
	}
	
	public void setDate(Date date)
	{
		if (date==null) {
			box.setText("");
			calendar.setSelectedDate(null);
		}
		else {
			box.setText(parsedString = format.format(date));
			calendar.setSelectedDate(date);
		}
	}
	
	public void validate()
	throws InvalidInputException
	{
		if (isEmpty()) {
			if (!emptyAllowed) {
				box.addStyleName(Styles.INVALID);
				throw new InvalidInputException();
			}
		}
		else if (getDate()==null) {
			box.addStyleName(Styles.INVALID);
			throw new InvalidInputException();
		}
		box.removeStyleName(Styles.INVALID);
	}
	
	public String getText()
	{
		return box.getText();
	}
	
	public void hideWeekends()
	{
		calendar.hideWeekends();
	}
	
	public void setEmptyAllowed(boolean allowed)
	{
		this.emptyAllowed = allowed;
	}
	
	public boolean isEmpty()
	{
		return getText()==null || getText().length()==0;
	}

}
